package com.dkts.oil.converter;


import com.alibaba.fastjson.JSONObject;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.grid.Grids;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.locationtech.jts.geom.Geometry;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.geometry.BoundingBox;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.LinkedList;
import java.util.List;

/**
 * @author Administrator
 */
public class UtilsGeoGrid {
    //1米=0.000012
    public static double gridLengthDefault=0.00012;
    /**
     * 获取栅格化后的 Geometry 集合
     *
     * @param srcGeometry    栅格前的 geometry
     * @param srcBoundingBox 栅格前的 boundingBox
     * @return 栅格化后的 BoundingBox 集合
     * @throws Exception 异常
     */
    public static List<Geometry> gridGeometryInfo(Geometry srcGeometry,
                                                  BoundingBox srcBoundingBox,
                                                  boolean isIntersects,
                                                  Double gridLength,
                                                  List<Geometry> geometriesObs,
                                                  String url,int targetGridCount,int  type) throws Exception {
        List<Geometry> geometries = new LinkedList<>();

        // 计算 Envelope 范围
        double width = srcBoundingBox.getMaxX() - srcBoundingBox.getMinX();
        double height = srcBoundingBox.getMaxY() - srcBoundingBox.getMinY();
        double area = width * height;

        double gridLengthTemp =  gridLengthDefault*gridLength;
        // 计算每个格子的边长（单位：度），目标是划分 targetGridCount 个格子
        double sideLen = Math.sqrt(area / targetGridCount);

        if (sideLen < gridLengthTemp){
            sideLen = gridLengthTemp;
        }

        sideLen = Math.max(sideLen, 0.0001); // 防止格子太小导致爆内存，设个下限值

        // 创建格子 Envelope
        ReferencedEnvelope envelope = new ReferencedEnvelope(
                srcBoundingBox.getMinX(), srcBoundingBox.getMaxX(),
                srcBoundingBox.getMinY(), srcBoundingBox.getMaxY(),
                DefaultGeographicCRS.WGS84
        );

        // 栅格化
        SimpleFeatureSource grid = type == 6 ? Grids.createHexagonalGrid(envelope, sideLen) : Grids.createSquareGrid(envelope, sideLen);

        SimpleFeatureCollection features = grid.getFeatures();
        SimpleFeatureIterator iterator = features.features();

        int i = 0;
        while (iterator.hasNext()) {
            SimpleFeature feature = iterator.next();
            Geometry geometry = (Geometry) feature.getDefaultGeometry();

            if (isIntersects) {
                if (srcGeometry.contains(geometry)) {
                    checkAndAddIfNoIntersection(geometry, geometriesObs, geometries);
                    // WriterToFile.writerFileGeoJson(url+"格子-"+i+".geojson", geometry);
                }
            } else {
                checkAndAddIfNoIntersection(geometry, geometriesObs, geometries);
                // WriterToFile.writerFileGeoJson(url+"格子-"+i+".geojson", geometry);
            }
            i++;
        }
        iterator.close();

        System.out.println("格子总数: " + i + "  符合条件格子数: " + geometries.size());
        return geometries;
    }


    public static boolean checkAndAddIfNoIntersection(Geometry srcGeometry, List<Geometry> geometriesObs, List<Geometry> geometries) {
        if (geometriesObs != null) {
            for (Geometry geometry : geometriesObs) {
                if (srcGeometry.intersects(geometry)) {
                    return false; // 有交集，不添加
                }
            }
        }
        geometries.add(srcGeometry); // 无交集，添加
        return true;
    }

    /**
     * 将米换算成纬度方向的度数
     */
    public static double metersToLatDegrees(double meters) {
        return meters / 111320.0;
    }

    /**
     * 将米换算成经度方向的度数（需传入所在纬度）
     */
    public static double metersToLonDegrees(double meters, double latitude) {
        double latitudeRadians = Math.toRadians(latitude);
        double meterPerDegreeLon = 111320.0 * Math.cos(latitudeRadians);
        return meters / meterPerDegreeLon;
    }

    /**
     * 获取栅格化后的 BoundingBox 集合
     *
     * @param srcGeometry    栅格前的 geometry
     * @param srcBoundingBox 栅格前的 boundingBox
     * @return 栅格化后的 BoundingBox 集合
     * @throws Exception 异常
     */
    public List<BoundingBox> gridGeometry(Geometry srcGeometry, BoundingBox srcBoundingBox) throws Exception {

        List<BoundingBox> boundingBoxes = new LinkedList<>();
        //获取栅格精度 (单位 度 °)
        double sideLen = getSideLen(srcBoundingBox);
        //获取 envelope
        ReferencedEnvelope envelope = getReferencedEnvelope(srcBoundingBox, sideLen);


        // max distance between vertices
        //double vertexSpacing = squareWidth / 20;

        //栅格化
        SimpleFeatureSource grid = Grids.createSquareGrid(envelope, sideLen);
        SimpleFeatureCollection features = grid.getFeatures();

        SimpleFeatureIterator iterator = features.features();
        while (iterator.hasNext()) {
            SimpleFeature feature = iterator.next();
            Geometry geometry = (Geometry) feature.getDefaultGeometry();

            //相交计算
            if (srcGeometry.intersects(geometry)) {
                BoundingBox bounds = feature.getBounds();
                boundingBoxes.add(bounds);
            }

        }
        return boundingBoxes;
    }

    //================================================= Gird ========================================================


    /**
     * 获取栅格化的 Envelope
     *
     * @param boundingBox 原 boundingBox
     * @param sideLen     栅格精度
     * @return Envelope
     */
    private static ReferencedEnvelope getReferencedEnvelope(BoundingBox boundingBox, double sideLen) {
        double maxX = getDoubleValueUp(boundingBox.getMaxX(), sideLen);
        double maxY = getDoubleValueUp(boundingBox.getMaxY(), sideLen);
        double minX = getDoubleValueDown(boundingBox.getMinX(), sideLen);
        double minY = getDoubleValueDown(boundingBox.getMinY(), sideLen);
        return new ReferencedEnvelope(minX, maxX, minY, maxY, DefaultGeographicCRS.WGS84);
    }


    /**
     * 获取栅格精度值
     *
     * @param boundingBox box
     * @return 栅格精度 以 度 为单位
     */
    public static double getSideLen(BoundingBox boundingBox) {
        double maxX = boundingBox.getMaxX();
        double maxY = boundingBox.getMaxY();
        double minX = boundingBox.getMinX();
        double minY = boundingBox.getMinY();
        double xBetween = Math.abs(new BigDecimal(Double.toString(maxX)).subtract(new BigDecimal(Double.toString(minX))).doubleValue());
        double yBetween = Math.abs(new BigDecimal(Double.toString(maxY)).subtract(new BigDecimal(Double.toString(minY))).doubleValue());
        // SIDE_LEN_SIZE =  边长格网数 可根据实际情况自定义
        double SIDE_LEN_SIZE = 50000;
        double result = new BigDecimal(Double.toString(Math.min(xBetween, yBetween))).divide(new BigDecimal(SIDE_LEN_SIZE)).doubleValue();
        result = Math.max(result, 0.0014);
        return Double.parseDouble(String.format("%.4f", result));
    }

    /**
     * 获取栅格精度值
     *
     * @param boundingBox box
     * @return 栅格精度 以 度 为单位
     */
    public static double getSideLen(BoundingBox boundingBox, int sideLenSize) {
        double maxX = boundingBox.getMaxX();
        double maxY = boundingBox.getMaxY();
        double minX = boundingBox.getMinX();
        double minY = boundingBox.getMinY();
        double xBetween = Math.abs(new BigDecimal(Double.toString(maxX)).subtract(new BigDecimal(Double.toString(minX))).doubleValue());
        double yBetween = Math.abs(new BigDecimal(Double.toString(maxY)).subtract(new BigDecimal(Double.toString(minY))).doubleValue());
        String dataInfo = Double.toString(Math.min(xBetween, yBetween));
        // SIDE_LEN_SIZE =  边长格网数 可根据实际情况自定义
        String result = new BigDecimal(dataInfo).divide(new BigDecimal(sideLenSize), 8, BigDecimal.ROUND_HALF_UP).toString();

        double resultInfo = Math.max(Double.valueOf(result), 0.0014);
        System.out.println(resultInfo);
        return Double.parseDouble(String.format("%.4f", resultInfo));
    }

    /**
     * 获取栅格精度值
     *
     * @return 栅格精度 以 度 为单位
     */
    public static double getSideLen(double max, double min, int sideLenSize) {
        String dataInfo = Double.toString(Math.min(min, max));
        BigDecimal bigDecimal = new BigDecimal(dataInfo).divide(new BigDecimal(sideLenSize), 4, BigDecimal.ROUND_HALF_UP);
        double result = bigDecimal.doubleValue();
        result = Math.max(result, 0.0014);
        return Double.parseDouble(String.format("%.4f", result));
    }

    /**
     * 向上取值
     *
     * @param value   值
     * @param sideLen 栅格精度
     * @return 值
     */
    private static double getDoubleValueUp(double value, double sideLen) {
        NumberFormat nf = NumberFormat.getNumberInstance();
        nf.setMaximumFractionDigits(2);
        nf.setRoundingMode(RoundingMode.UP);
        String format = nf.format(value);
        return Math.min(Double.parseDouble(format) + sideLen, 180);
    }

    /**
     * 向下取值
     *
     * @param value   值
     * @param sideLen 栅格精度
     * @return 值
     */
    private static double getDoubleValueDown(double value, double sideLen) {
        NumberFormat nf = NumberFormat.getNumberInstance();
        nf.setMaximumFractionDigits(2);
        nf.setRoundingMode(RoundingMode.DOWN);
        String format = nf.format(value);
        return Math.max(Double.parseDouble(format) - sideLen, -180);
    }

}
